#ifndef __BOAT_DISPATCHER_HPP__
#define __BOAT_DISPATCHER_HPP__

#include "globaldef.hpp"
#include "PlanForSea.hpp"
#include "Map.hpp"
#include <list>
#include "Boat.hpp"
#include "Coord.hpp"

/**
 * @brief 船调度器
 */
class BoatDispatcher
{
private:
    struct cmd_t {
        enum type_t {
            INVALID, // 无效指令
            WAIT, // 等待指令
            MOVE, // 移动指令
        } type = INVALID;
        int data = INVALID_BERTH_ID; // 终点港口号或离港时间
        cmd_t(type_t type, int data):
            type(type), data(data) {

        }
    };

public:
    Map &map; // 地图
    Boat *boat = nullptr; // 船
    PlanForSea plan; // 正在执行的方案
    std::list<cmd_t> cmdList; // 指令表
    bool isLock = false; // 是否禁用
    #ifdef USE_LOG
    static int sellCnt; // 所有船前往虚拟点的总次数
    static int sellGoodsCnt; // 所有船出售货物的总数
    #endif

public:
    /**
     * @brief 构造函数
     * @param map 
     */
    explicit BoatDispatcher(Map &map):
        map(map) {

    }
    /**
     * @brief 连接到船
     * @param boat 
     */
    void attach(Boat *boat) {
        this->boat = boat;
    }
    /**
     * @brief 锁定该分配器，使其表现为一直处于忙碌状态
     */
    void lock() {
        isLock = true;
    }
    /**
     * @brief 是否空闲
     * @return true 
     * @return false 
     */
    bool isFree() const {
        return !isLock && !plan.isValid();
    }
    /**
     * @brief 工作循环
     * @param frameID
     */
    void run(int frameID) {
        if (plan.isValid()) {
            int loopTimes = 1; // 循环次数
            do {
                if (cmdList.empty()) {
                    plan.setValidate(false); // 指令执行完，方案完成
                    break;
                }
                auto runningCmd = cmdList.front();
                switch (runningCmd.type) {
                // 无效指令，不作任何操作
                default:
                    cmdList.pop_front(); // 弹出指令
                    ++loopTimes; // 再循环一次，实现指令的同帧连续执行
                    break;
                // 移动指令，前往指定泊位
                case cmd_t::MOVE:
                    if (boat->berthID != runningCmd.data) { // 船还没有开始执行动作
                        #ifdef USE_LOG
                        // LOG("[%05d] Boat[%d] [%3d/%03u] [%2d]->[%2d]\n", frameID, boat->id, boat->getGoodsNum(), boat->volume, boat->berthID, runningCmd.data);
                        if (runningCmd.data == VIRTUAL_BERTH_ID) {
                            ++sellCnt;
                            sellGoodsCnt += boat->getGoodsNum();
                        }
                        #endif
                        boat->move(runningCmd.data);
                        if (runningCmd.data == VIRTUAL_BERTH_ID) { // 记录上次离开虚拟点的时间
                            boat->leaveVirtualPointTime = frameID;
                        }
                    } else {
                        switch (boat->state) { // 检查船的当前状态
                        // 移动中或等待中，不作任何处理
                        default:
                        case BOAT_MOVING:
                        case BOAT_WAITING:
                            break;
                        // 开始装卸货，说明指令执行完毕
                        case BOAT_LOADING:
                            cmdList.pop_front(); // 弹出指令
                            ++loopTimes; // 再循环一次，执行下一条指令
                            break;
                        }
                    }
                    break;
                // 等待指令，当前时间到达指定帧时结束
                case cmd_t::WAIT:
                    if (boat->isFull() || frameID > runningCmd.data - 1) { // 在指定帧开始执行另一个指令，因此要-1
                        cmdList.pop_front(); // 弹出指令
                        ++loopTimes; // 再循环一次，执行下一条指令
                    }
                    break;
                }
            } while (--loopTimes > 0);
        }
    }
    /**
     * @brief 设置方案
     * @param plan 
     */
    void setPlan(const PlanForSea &plan) {
        if (!plan.isValid()) {
            return;
        }
        this->plan = plan;
        cmdList.push_back(cmd_t(cmd_t::MOVE, plan.berthID)); // 进入泊位开始装货
        cmdList.push_back(cmd_t(cmd_t::WAIT, plan.leaveTime)); // 装货到指定时间
        if (plan.autoSell) {
            cmdList.push_back(cmd_t(cmd_t::MOVE, VIRTUAL_BERTH_ID)); // 前往虚拟点卸货
        }
    }
};

#ifdef USE_LOG
int BoatDispatcher::sellCnt = 0; // 所有船前往虚拟点的总次数
int BoatDispatcher::sellGoodsCnt = 0; // 所有船出售货物的总数
#endif

#endif // __BOAT_DISPATCHER_HPP__